unit Checkers.Renderer;

interface

uses
  Classes,
  SysUtils,
  // GLScene
  VectorGeometry,
  VectorTypes,
  GLScene,
  GLRenderContextInfo,
  GLContext,
  GLState,
  GLUtils,
  GLTextureFormat,
  GLTexture,
  // Project
  Checkers.Visualizer,
  Checkers.Field,
  Checkers.Borders,
  Checkers.Camera;

type
  TRenderer = class(TGLBaseSceneObject)
  private
    FCheckers: TCheckersList;
    FFragment: AnsiString;
    FVertex: AnsiString;
    FField: TField;
    FBorder: TPlanesList;
    FIsShaderReady: Boolean;
    FShader: TGLProgramHandle;
    FUseShaders: Boolean;
    FCamera: TCheckersCamera;
    procedure SetCheckers(const Value: TCheckersList);
    procedure SetField(const Value: TField);
    procedure SetBorder(const Value: TPlanesList);
  public
    constructor Create(AOwner: TComponent); override;
    procedure InitShader;
    procedure DoRender(var ARci: TRenderContextInfo;
      ARenderSelf, ARenderChildren: Boolean); override;
    destructor Destroy; override;
    property Checkers: TCheckersList write SetCheckers;
    property Field: TField write SetField;
    property Border: TPlanesList write SetBorder;
    property UseShaders: Boolean read FUseShaders write FUseShaders;
  end;

procedure InitRenderer(const AOwner: TGLBaseSceneObject;
  const ACamera: TCheckersCamera);
function CheckersRenderer: TRenderer;

implementation

var
  vRenderer: TRenderer;

const
  cShaders = 'Shaders\';
  cVertex = cShaders + 'common.vs';
  cFragment = cShaders + 'Checker.fs';

procedure InitRenderer(const AOwner: TGLBaseSceneObject;
  const ACamera: TCheckersCamera);
begin
  if not Assigned(vRenderer) then
  begin
    vRenderer := TRenderer.CreateAsChild(AOwner);
    vRenderer.FCamera := ACamera;
  end;
end;

function CheckersRenderer: TRenderer;
begin
  Result := vRenderer;
end;

{ TRenderer }

constructor TRenderer.Create(AOwner: TComponent);
begin
  inherited;
  FUseShaders := False;
end;

procedure TRenderer.DoRender(var ARci: TRenderContextInfo;
  ARenderSelf, ARenderChildren: Boolean);
var
  i: Integer;
begin
  if FUseShaders then
  begin
    if Assigned(FShader) and not Assigned(FShader.RenderingContext) then
      FIsShaderReady := False;
    if not FIsShaderReady then
    begin
      InitShader;
      FIsShaderReady := True;
    end;

    with ARci.GLStates do
    begin
      Enable(stBlend);
      Enable(stAlphaTest);
      Disable(stLighting);
      SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
      SetGLAlphaFunction(cfGreater, 0);
      Enable(stDepthTest);
      DepthWriteMask := True;
    end;
    FShader.UseProgramObject;

    with ARci.PipelineTransformation do
    begin
      // FShader.Uniform4f['lightPos'] := VectorTransform(VectorMake(0, 100, 0, 1), ViewMatrix);
      // FShader.Uniform4f['eyePos'] := VectorTransform(FCamera.AbsolutePosition, ViewMatrix);
    end;
    FShader.Uniform4f['uColor'] := WHmgVector;
    FShader.Uniform1f['uBrightnes'] := 1.0;
    FShader.UniformTextureHandle['uTexture', 0, ttTexture2D] :=
      FField.WhiteCells[0].Material.Texture.Handle;
    for i := 0 to FField.WhiteCells.Count - 1 do
      FField.WhiteCells[i].Render(ARci);

    FShader.UniformTextureHandle['uTexture', 0, ttTexture2D] :=
      FField.BlackCells[0].Material.Texture.Handle;
    for i := 0 to FField.WhiteCells.Count - 1 do
      FField.BlackCells[i].Render(ARci);

    FShader.EndUseProgramObject;

    FField.Cells[0].HighlitePlane.Material.Texture.Apply(ARci);
    for i := 0 to FField.Cells.Count - 1 do
    begin
      if FField.Cells[i].IsHighlited then
      begin
        GL.Color4fv(FField.Cells[i].HighlitePlane.Material.FrontProperties.Diffuse.AsAddress);
        FField.Cells[i].RenderDecals(ARci);
      end;
    end;
    FField.Cells[0].HighlitePlane.Material.Texture.UnApply(ARci);
    for i := 0 to FField.Cells.Count - 1 do
      FField.Cells[i].RenderText(ARci);

    FShader.UseProgramObject;

    FShader.UniformTextureHandle['uTexture', 0, ttTexture2D] := 0;
    for i := 0 to FCheckers.Count - 1 do
    begin
      if FCheckers[i].ViewType <> 0 then
      begin
        FShader.Uniform4f['uColor'] := FCheckers[i].Color;
        FCheckers[i].DoRender(ARci, True, True);
      end;
    end;
    FShader.Uniform4f['uColor'] := WHmgVector;
    for i := 0 to FBorder.Count - 1 do
    begin
      FShader.UniformTextureHandle['uTexture', 0, ttTexture2D] :=
        FBorder[i].Material.Texture.Handle; // FTest.Handle;
      FBorder[i].Render(ARci);
    end;
    FShader.EndUseProgramObject;

    with ARci.GLStates do
    begin
      Disable(stBlend);
      Disable(stAlphaTest);
    end;
  end
  else
  begin
    FField.WhiteCells[0].Material.Texture.Apply(ARci);
    for i := 0 to FField.WhiteCells.Count - 1 do
      FField.WhiteCells[i].Render(ARci);

    FField.BlackCells[0].Material.Texture.Apply(ARci);
    for i := 0 to FField.BlackCells.Count - 1 do
      FField.BlackCells[i].Render(ARci);
    FField.BlackCells[0].Material.Texture.UnApply(ARci);

    with ARci.GLStates do
    begin
      Enable(stBlend);
      Enable(stAlphaTest);
      Disable(stLighting);
      SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
      SetGLAlphaFunction(cfGreater, 0);
      Enable(stDepthTest);
      DepthWriteMask := True;
    end;

    FField.Cells[0].HighlitePlane.Material.Texture.Apply(ARci);
    for i := 0 to FField.Cells.Count - 1 do
    begin
      if FField.Cells[i].IsHighlited then
      begin
        GL.Color4fv(FField.Cells[i].HighlitePlane.Material.FrontProperties.Diffuse.AsAddress);
        FField.Cells[i].RenderDecals(ARci);
      end;
    end;
    FField.Cells[0].HighlitePlane.Material.Texture.UnApply(ARci);
    for i := 0 to FField.Cells.Count - 1 do
      FField.Cells[i].RenderText(ARci);

    ARci.GLStates.Enable(stLighting);

    for i := 0 to FCheckers.Count - 1 do
    begin
      if FCheckers[i].ViewType <> 0 then
      begin
        ARci.GLStates.SetGLMaterialColors(cmFront, WHmgVector,
          VectorMake(0.3, 0.3, 0.3, 1), FCheckers[i].Color, XYZWHMGVector, 0);
        FCheckers[i].DoRender(ARci, True, True);
      end;
    end;

    with ARci.GLStates do
    begin
      Disable(stBlend);
      Disable(stAlphaTest);
      Disable(stLighting);
    end;

    for i := 0 to FBorder.Count - 1 do
    begin
      FBorder[i].Material.Texture.Apply(ARci);
      FBorder[i].Render(ARci);
      FBorder[i].Material.Texture.UnApply(ARci);
    end;
  end;
end;

procedure TRenderer.InitShader;
begin
  if Length(FVertex) <= 1 then
  begin
    FVertex := LoadAnsiStringFromFile(cVertex);
    FFragment := LoadAnsiStringFromFile(cFragment);
  end;
  FShader := TGLProgramHandle.CreateAndAllocate();
  FShader.AddShader(TGLVertexShaderHandle, String(FVertex));
  FShader.AddShader(TGLFragmentShaderHandle, String(FFragment));
  if not FShader.LinkProgram then
    raise Exception.Create(FShader.InfoLog);
  if not FShader.ValidateProgram then
    raise Exception.Create(FShader.InfoLog);
end;

procedure TRenderer.SetBorder(const Value: TPlanesList);
begin
  FBorder := Value;
end;

procedure TRenderer.SetField(const Value: TField);
begin
  FField := Value;
end;

procedure TRenderer.SetCheckers(const Value: TCheckersList);
begin
  FCheckers := Value;
end;

destructor TRenderer.Destroy;
begin
  FShader.Free;
  inherited;
end;

initialization

finalization

end.
